package action;

import annotation.LineMapping;
import annotation.Liner;
import exception.ScanMappingException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Description:
 * Created by cjq on 2018/1/26
 */
public class MappingScanner {

    private static Logger logger = LogManager.getLogger(MappingScanner.class);

    public static final Map<Integer ,LineMethod> methodMapper = new HashMap<Integer, LineMethod>();

    public void invoke(int action , Object retuneValue , Object...params){

    }

    public static void scanPackage(String packageName){
        //获取所有类
        List<Class<?>> classes  = ClassUtil.getAllClassesByPackageName(packageName);
        //过滤得到具有Liner注解的类
        classes = getClassesWithAnnoLiner(classes);
        //扫描类中具有@LineMethod注解的方法
        try {
            logger.debug("MappingScanner is scanning @Liner.@LineMethod under package "+packageName);
            scanClass(classes);
        } catch (ScanMappingException e) {
            logger.error(e);
        } catch (ClassNotFoundException e) {
            logger.error(e);
        }
    }

    /**
     * 过滤没有特定注解的class
     * @param classes  类
     * @return
     */
    private static List<Class<?>> getClassesWithAnnoLiner(List<Class<?>> classes){
        Iterator<Class<?>> iterator = classes.iterator();
        while (iterator.hasNext()){
            if (iterator.next().getAnnotation(Liner.class) == null)
                iterator.remove();
        }
        return classes;
    }


    private static void scanClass(List<Class<?>>classes) throws ScanMappingException, ClassNotFoundException {
        if (classes.size() < 1){
            throw new ScanMappingException("No annotated classes");
        }
        for (Class c : classes){
            Method[] methods = c.getMethods();
            if (methods.length < 1){
                throw new ScanMappingException("No methods in class "+c.getName());
            }
            for (Method method : methods){
                LineMapping annotation = method.getAnnotation(LineMapping.class);
                if (annotation == null)
                    continue;
                int action = annotation.value();
                if (methodMapper.containsKey(action)){
                    throw new ScanMappingException("Duplicate LineMapper's value in method " + c.getName()+"." + method.getName());
                }
                logger.debug("\taction "+action + " "+method.getName());
                methodMapper.put(action,new LineMethod(method,c));
            }
        }
    }
}
